package com.jzo2o.market.service.impl;

import cn.hutool.db.DbRuntimeException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.market.dto.request.CouponUseBackReqDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.domain.CouponWriteOff;
import com.jzo2o.market.model.dto.request.CouponOperationPageQueryReqDTO;
import com.jzo2o.market.model.dto.request.SeizeCouponReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.CouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponUseBackService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.market.utils.CouponUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageUtils;
import com.jzo2o.redis.utils.RedisSyncQueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.jzo2o.common.constants.ErrorInfo.Code.SEIZE_COUPON_FAILD;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Resource(name = "seizeCouponScript")
    private DefaultRedisScript<String> seizeCouponScript;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponUseBackService couponUseBackService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;

    @Resource
    private CouponMapper couponMapper;

    /**
     * 优惠券领取 列表
     *
     * @param reqDTO 领取参数
     * @return 领取结果
     */

    @Override
    public PageResult<CouponInfoResDTO> couponPage(CouponOperationPageQueryReqDTO reqDTO) {
        Page<Coupon> couponPage = PageUtils.parsePageQuery(reqDTO, Coupon.class);
        couponPage = super.page(couponPage,
                new LambdaQueryWrapper<Coupon>()
                        .eq(Coupon::getActivityId, reqDTO.getActivityId())
        );
        return PageUtils.toPage(couponPage, CouponInfoResDTO.class);
    }

    /**
     * 获取用户优惠券列表
     *
     * @param status 优惠券状态 1:未使用，2:已使用，3:已过期
     * @param lastId 最后一个id
     * @return
     */
    @Override
    public List<CouponInfoResDTO> getMyList(Integer status, Long lastId) {
        Long userId = UserContext.currentUserId();
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Coupon::getUserId, userId)
                .eq(Coupon::getStatus, status)
                .gt(lastId != null, Coupon::getId, lastId);
        List<Coupon> list = super.list(queryWrapper);
        return BeanUtils.copyToList(list, CouponInfoResDTO.class);
    }

    /**
     * 领取优惠券
     *
     * @param reqDTO 领取参数
     * @return 领取结果
     */
    @Override
    @Transactional
    public void seizeCoupon(SeizeCouponReqDTO reqDTO) {
        //校验活动
        Activity activity = activityService.queryActivityFromCacheById(reqDTO.getId());
        if(activity == null || activity.getDistributeStartTime().isAfter(DateUtils.now())){
            throw new CommonException("活动未开始");
        }
        if(activity.getDistributeEndTime().isBefore(DateUtils.now())){
            throw new CommonException("活动已结束");
        }
        //利用Redis原自减，如果减出来小于0，那么就表示没有库存了
        Long afterSub = redisTemplate.opsForValue().decrement(COUPON_RESOURCE_STOCK + reqDTO.getId(), 1);
        // log.info("活动{}扣减库存后：{}",activity.getName(),afterSub);
        if (afterSub < 0) {
            //将刚才减的加回去，这一步可以不要，但是这样预热中的库存会是负数，看着难受
            redisTemplate.opsForValue().increment(COUPON_RESOURCE_STOCK + reqDTO.getId(), 1);
            throw new CommonException("抢光了");
        }
        //更新当前库存
        int flag = 0;
        try {
            flag = activityService.updateActivityStock(reqDTO.getId(), 1);
        } catch (Exception e) {
            redisTemplate.opsForValue().increment(COUPON_RESOURCE_STOCK + reqDTO.getId(), 1);
            log.error("更新数据库库存失败", e);
            throw new CommonException("抢卷失败");
        }
        if (flag == 0) {
            throw new CommonException("抢光啦");
        }
        log.info("更新数据库库存成功，开始记录coupon表");
        //插入优惠券表
        addToCoupon(activity);
        log.info("抢卷成功");
    }

    /**
     * 插入优惠券表
     *
     * @param activity
     */
    private void addToCoupon(Activity activity) {
        Coupon coupon = new Coupon();
        //不能把activity的id复制给coupon的id
        BeanUtils.copyProperties(activity, coupon, "id");
        //设置当前用户信息
        CurrentUserInfo currentUserInfo = UserContext.currentUser();
        coupon.setUserId(currentUserInfo.getId());
        coupon.setUserName(currentUserInfo.getName());
        coupon.setStatus(CouponStatusEnum.NO_USE.getStatus());
        coupon.setActivityId(activity.getId());
        //设置有效期
        coupon.setValidityTime(LocalDateTime.now().minusDays(activity.getValidityDays()));
        save(coupon);
    }
}
